package com.spring.context;

import com.spring.annotation.Autowired;
import com.spring.aop.AdviceSupport;
import com.spring.aop.AopConfig;
import com.spring.aop.ProxyFactory;
import com.spring.bean.BeanDefinition;
import com.spring.bean.BeanWrapper;
import com.spring.factory.BeanDefinitionReader;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * @author SpringWang
 * @date 2021/6/2
 */
public class ApplicationContext {

    private final String[] configLocations;
    // 解析配置文件工具类
    private BeanDefinitionReader beanDefinitionReader;
    // beanName+className 的缓存
    private final Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
    // IOC容器
    private final Map<String, BeanWrapper> factoryBeanInstanceCache = new HashMap<>();
    // 原生对象缓存
    private final Map<String, Object> factoryBeanObjectCache = new HashMap<>();


    public ApplicationContext(String... configLocations) {
        this.configLocations = configLocations;

        try {
            // 1.读取配置文件并解析成BeanDefinition对象
            BeanDefinitionReader reader = new BeanDefinitionReader(configLocations);
            List<BeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
            // 2.将解析后的BeanDefinition对象注册到beanDefinitionMap中
            registerBeanDefinitions(beanDefinitions);
            // 3.创建bean对象，调用getBean()
            createBean();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void registerBeanDefinitions(List<BeanDefinition> beanDefinitions) {
        for (BeanDefinition beanDefinition : beanDefinitions) {
            if (beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
                throw new IllegalStateException("Bean " + beanDefinition.getFactoryBeanName() + " has already exists");
            }
            beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
            beanDefinitionMap.put(beanDefinition.getBeanClassName(), beanDefinition);
        }
    }

    private void createBean() {
        beanDefinitionMap.forEach((k, v) -> getBean(k));
    }

    /**
     * 真正触发IoC的动作
     * 1.创建bean
     * 2.依赖注入
     *
     * @param beanName beanName
     */
    private Object getBean(String beanName) {
        // 1.创建实例
        // 1.1 获取配置信息，拿到beanDefinition
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        // 用反射创建实例，实例有可能是原生对象，也可能是代理对象，封装成BeanWrapper统一处理
        Object instance = instantiateBean(beanName, beanDefinition);
        BeanWrapper beanWrapper = new BeanWrapper(instance);

        factoryBeanInstanceCache.put(beanName, beanWrapper);
        // 依赖注入
        populateBean(beanName, beanDefinition, beanWrapper);

        return beanWrapper.getInstance();
    }

    /**
     * 使用反射实例化对象
     */
    private Object instantiateBean(String beanName, BeanDefinition beanDefinition) {
        String className = beanDefinition.getBeanClassName();
        Object instance = null;
        try {
            Class<?> aClass = Class.forName(className);
            instance = aClass.getConstructor().newInstance();
            // 接入AOP
            AdviceSupport support = instantiateAopConfig(beanDefinition);
            support.setTargetClass(aClass);
            support.setTarget(instance);
            // 如果需要代理，则用代理对象覆盖目标对象
            if (support.pointCutMatch()) {
                instance = new ProxyFactory(support).getProxy();
            }

            factoryBeanObjectCache.put(beanName, instance);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    // fixme spring需不需要传beanDefinition?
    private AdviceSupport instantiateAopConfig(BeanDefinition beanDefinition) {
        AopConfig aopConfig = AopConfig.create(beanDefinitionReader.getConfig().getProperty("pointCut"),
                beanDefinitionReader.getConfig().getProperty("aspectClass"),
                beanDefinitionReader.getConfig().getProperty("aspectBefore"),
                beanDefinitionReader.getConfig().getProperty("aspectAfter"),
                beanDefinitionReader.getConfig().getProperty("aspectAfterThrowing"),
                beanDefinitionReader.getConfig().getProperty("aspectAfterThrowingName"));
        return new AdviceSupport(aopConfig);
    }

    /**
     * 依赖注入
     * fixme 多余参数作用？
     */
    private void populateBean(String beanName, BeanDefinition beanDefinition, BeanWrapper beanWrapper) {
        Object instance = beanWrapper.getInstance();
        Class<?> aClass = beanWrapper.getWrapperClass();
        // 获取到所有字段
        for (Field field : aClass.getDeclaredFields()) {
            // 如果没加Autowired注解的属性跳过
            if (!field.isAnnotationPresent(Autowired.class)) {
                continue;
            }
            Autowired annotation = field.getAnnotation(Autowired.class);
            String autowiredBeanName = annotation.value().trim();
            if ("".equals(autowiredBeanName)) {
                autowiredBeanName = field.getType().getName();
            }
            field.setAccessible(true);
            try {
                if (factoryBeanInstanceCache.get(autowiredBeanName) == null) {
                    continue;
                }
                // 赋值
                field.set(instance, this.factoryBeanInstanceCache.get(autowiredBeanName).getInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    public int getBeanDefinitionCount() {
        return beanDefinitionMap.size();
    }

    public Properties getConfig() {
        return beanDefinitionReader.getConfig();
    }
}
